// Tencent is pleased to support the open source community by making
// 蓝鲸智云 - 监控平台/日志平台 (BlueKing - Monitor/Log) available.
// Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. All rights reserved.
// Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://opensource.org/licenses/MIT
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//

package define

import (
	"net"
	"strings"

	"go.opentelemetry.io/collector/model/pdata"
)

type RecordType string

const (
	RecordUndefined      RecordType = "undefined"
	RecordTraces         RecordType = "traces"
	RecordMetrics        RecordType = "metrics"
	RecordLogs           RecordType = "logs"
	RecordTracesDerived  RecordType = "traces.derived"
	RecordMetricsDerived RecordType = "metrics.derived"
	RecordLogsDerived    RecordType = "logs.derived"
)

// IntoRecordType 将字符串描述转换为 RecordType 并返回是否为 Derived 类型
func IntoRecordType(s string) (RecordType, bool) {
	var t RecordType
	switch s {
	case string(RecordTraces):
		t = RecordTraces
	case string(RecordMetrics):
		t = RecordMetrics
	case string(RecordLogs):
		t = RecordLogs
	case string(RecordTracesDerived):
		t = RecordTracesDerived
	case string(RecordMetricsDerived):
		t = RecordMetricsDerived
	case string(RecordLogsDerived):
		t = RecordLogsDerived
	default:
		t = RecordUndefined
	}
	return t, strings.HasSuffix(s, ".derived")
}

// RequestType 标记请求类型：Http、Grpc 用于后续做统计
type RequestType string

const (
	RequestHttp      RequestType = "http"
	RequestGrpc      RequestType = "grpc"
	RequestDerived   RequestType = "derived"
	RequestBenchmark RequestType = "benchmark"
)

type RequestClient struct {
	IP string
}

func ParseRequestIP(source string) string {
	ipstr, _, err := net.SplitHostPort(source)
	if err == nil {
		return ipstr
	}
	ip := net.ParseIP(source)
	if ip != nil {
		return ip.String()
	}
	return ""
}

// Record 是 Processor 链传输的数据类型
type Record struct {
	RecordType    RecordType
	RequestType   RequestType
	RequestClient RequestClient
	Token         Token
	Data          interface{}
}

func (r *Record) Unwrap() {
	switch r.RecordType {
	case RecordTracesDerived:
		r.RecordType = RecordTraces
	case RecordMetricsDerived:
		r.RecordType = RecordMetrics
	case RecordLogsDerived:
		r.RecordType = RecordLogs
	}
}

func (r *Record) Clone() *Record {
	cloned := &Record{
		RecordType:    r.RecordType,
		RequestType:   r.RequestType,
		RequestClient: r.RequestClient,
		Token:         r.Token,
	}

	switch r.RecordType {
	case RecordTraces:
		if rt, ok := r.Data.(pdata.Traces); ok {
			cloned.Data = rt.Clone()
		}
	case RecordMetrics:
		if rm, ok := r.Data.(pdata.Metrics); ok {
			cloned.Data = rm.Clone()
		}
	case RecordLogs:
		if rl, ok := r.Data.(pdata.Logs); ok {
			cloned.Data = rl.Clone()
		}
	}
	return cloned
}

type RecordData struct {
	Count int
	Data  interface{}
}

type PushMode string

const (
	PushModeGuarantee  PushMode = "guarantee"
	PushModeDropIfFull PushMode = "dropIfFull"
)

type RecordQueue struct {
	records chan *Record
	mode    PushMode
}

// NewRecordQueue 生成 Records 消息队列
func NewRecordQueue(mode PushMode) *RecordQueue {
	return &RecordQueue{
		mode:    mode,
		records: make(chan *Record, CoreNums()*QueueAmplification),
	}
}

func (q *RecordQueue) Push(r *Record) {
	switch q.mode {
	case PushModeGuarantee:
		q.records <- r
	case PushModeDropIfFull:
		select {
		case q.records <- r:
		default:
		}
	}
}

func (q *RecordQueue) Get() <-chan *Record {
	return q.records
}

// Token 描述了 Record 校验的必要信息
type Token struct {
	Original      string
	MetricsDataId int32
	TracesDataId  int32
	LogsDataId    int32
	BizId         int32
	AppName       string
}

func (t Token) Get(rtype RecordType) int32 {
	switch rtype {
	case RecordTraces:
		return t.TracesDataId
	case RecordMetrics:
		return t.MetricsDataId
	case RecordLogs:
		return t.LogsDataId
	}
	return -1
}
